package top.xtcoder.xtpsd.utils;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;

import top.xtcoder.xtpsd.log.Log;

public class ByteUtil {
	private static final Log log = Log.getLog(ByteUtil.class);
	/**
	 * 从 byte 数组中解析出 int 数据
	 * @param bs
	 * @return
	 */
	public static int bytesToInt(byte[] bs) {
		StringBuffer sb = new StringBuffer();
		for(int i = 0; i < bs.length; i ++) {
			String str = Integer.toBinaryString((bs[i] & 0xFF) + 0x100).substring(1);
			sb.append(str);
		}
		int n = Integer.parseInt(sb.toString(), 2);
		return n;
	} 
	
	public static int readByteToInt(InputStream inputStream, int num) throws IOException {
    	byte[] bs = new byte[num];
		inputStream.read(bs);
		int val = ByteUtil.bytesToInt(bs);
		return val;
    }
	
    public static long bytesToLong(byte[] bs){
    	StringBuffer sb = new StringBuffer();
		for(int i = 0; i < bs.length; i ++) {
			String str = Integer.toBinaryString((bs[i] & 0xFF) + 0x100).substring(1);
			sb.append(str);
		}
		long n = Long.parseLong(sb.toString(), 2);
		return n;
    }
    
    public static long readByteToLong(InputStream inputStream, int num) throws IOException {
    	byte[] bs = new byte[num];
		inputStream.read(bs);
		long val = ByteUtil.bytesToLong(bs);
		return val;
    }
    
    public static double bytesToDouble(byte[] arr) throws IOException {
    	long value = 0;
        for (int i = 0; i < arr.length; i++) {
            value |= ((long) (arr[i] & 0xff)) << (arr.length * i);
        }
        return Double.longBitsToDouble(value);
    }
    
    public static double readByteToDouble8(InputStream is) throws IOException {
    	byte[] bs = new byte[8];
    	is.read(bs);
    	ByteBuffer buffer = ByteBuffer.wrap(bs);
    	return buffer.getDouble();
    }
    
    public static double readByteToFloat4(InputStream is) throws IOException {
    	byte[] bs = new byte[4];
    	is.read(bs);
    	ByteBuffer buffer = ByteBuffer.wrap(bs);
    	return buffer.getFloat();
    }
    
    public static String readByteToStr(InputStream inputStream, 
    		int num,
    		String chatset) throws IOException {
    	try {
	    	byte[] bs = new byte[num];
	    	inputStream.read(bs);
	    	String str = new String(bs, chatset);
	    	return str.trim();
    	}catch(Exception e) {
    		log.error(e, "num=%d, available=%d", num, inputStream.available());
    		return "";
    	}

//    	ByteArrayOutputStream baos = new ByteArrayOutputStream();
//    	
//    	int canReadSize = num;
//    	byte[] bs = new byte[1024];
//    	while(canReadSize > 0) {
//    		int readSize = inputStream.read(bs, 0, Math.min(canReadSize, 1024));
//    		baos.write(bs, 0, Math.min(canReadSize, 1024));
//    		
//    		canReadSize -= readSize;
//    	}
//    	String str = baos.toString(StandardCharsets.UTF_8.name());
//		return str;
    	
    }
    
    public static String readByteToStr(InputStream inputStream, 
    		int num) throws IOException {
    	String str = readByteToStr(inputStream, num, "gbk");
    	return str.trim(); 
    }
    
    public static void viewStreamByteInfo(
    		Log selfLog,
    		String id,
    		String type,
    		InputStream inputStream) throws IOException {
    	viewStreamByteInfo(selfLog, id, type, inputStream, inputStream.available());
    }
    
    public static void viewStreamByteInfo(
    		Log selfLog,
    		String id,
    		String type,
    		InputStream inputStream, 
    		int num) throws IOException {
    	num = Math.min(Math.min(num, 1000), inputStream.available());
    	try {
    		inputStream.mark(num);
	    	byte[] bs = new byte[num];
	    	inputStream.read(bs);
	    	String str = new String(bs, "gbk");
	    	inputStream.reset();
	    	
	    	String msg = new String("[字节信息查看 id=【%s】 type=【%s】]还未解析，后面一段字节的信息\nStringContent=%s\nArrayContent=%s");
	    	selfLog.log(msg, id, type, str, Arrays.toString(bs));
    	}catch(Exception e) {
    		selfLog.error(e, "num=%d, available=%d", num, inputStream.available());
    	}
    }
    
    public static String bytesToUnicodeStr(byte[] byteArray) {
    	String unicodeString = null;
        try {
            ByteArrayInputStream byteStream = new ByteArrayInputStream(byteArray);
            DataInputStream dataStream = new DataInputStream(byteStream);

            StringBuilder stringBuilder = new StringBuilder();
            while (dataStream.available() > 0) {
                char unicodeChar = dataStream.readChar();
                stringBuilder.append(unicodeChar);
            }

            unicodeString = stringBuilder.toString();
            dataStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        if(unicodeString != null) {
        	unicodeString = unicodeString.trim();
        }
        return unicodeString;
    }
    
    public static String readByteToUnicodeStr(InputStream is, int num) throws IOException {
    	byte[] bs = new byte[num];
    	is.read(bs);
        return bytesToUnicodeStr(bs);
    }
    
    public static String readByteToUnicodeStr(InputStream is) throws IOException {
    	int length = readByteToInt(is, 4);
    	return readByteToUnicodeStr(is, length * 2);
    }
    
    public static String readByteToPascalStr(InputStream is) throws IOException {
    	int length = readByteToInt(is, 1);
    	if(length <= 0) {
    		return "";
    	}
    	return readByteToStr(is, length);
    }
    
    public static String readByteToClassId(InputStream is) throws IOException {
    	int length = readByteToInt(is, 4);
    	length = length < 4 ? 4 : length;
    	return readByteToStr(is, length);
    }
}
